package Talk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author shkstart
 * @creat 2022-05-14:17
 */
public class ChatServer implements Constants{
    /**
     * 保存所有客户端套接字对象Socket
     */
    private static final List<Socket>  socketList = new CopyOnWriteArrayList<>();
    /**
     * 保存当前客户端套接字对象
     */
    private static  Socket currentSocket = null;
    /**
     * newFixedThreadPool(30):创建一个固定数量的线程池
     */
    private static final ExecutorService executor = Executors.newFixedThreadPool(30);
    /**
     * 启动聊天室服务器
     */
    private static void startServer(int serverPort){
        ServerSocket chatRoomServer = null;
        try {
            chatRoomServer = new ServerSocket(serverPort);
            System.out.printf("聊天室已启动,正在侦听%d端口，等待客户端连接请求... %n",serverPort);
            while (true){
                currentSocket = chatRoomServer.accept();
                //获得远程客户端IP和端口Port
                String remoteIp = currentSocket.getInetAddress().getHostAddress();
                int remotePort = currentSocket.getPort();
                System.out.printf("%s/%d客户端已上线 %n",remoteIp,remotePort);
                //将成共连接的客户端保存到List容器中
                socketList.add(currentSocket);
                System.out.printf("当前聊天室在线人数为：%d %n",socketList.size());
                //为成功连接的客户端提供专门的工作线程服务
                executor.submit(new ServerTaskHandler(currentSocket));

            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 静态内部类 处理当前客户端发送过来的消息，将接收到的消息发给每一个客户端
     */
    private static class ServerTaskHandler implements Runnable{
        private Socket currentSocket;

        public ServerTaskHandler(final Socket currentSocket){
            this.currentSocket = currentSocket;
        }

        @Override
        public void run() {
            //线程执行体
            BufferedReader reader = null;
            while (true){
                try {
                    reader  = new BufferedReader(new InputStreamReader(currentSocket.getInputStream(), Charset.defaultCharset()));
                    //读取当前客户端的信息
                    String msg = reader.readLine();
                    dispatch(msg);
                } catch (IOException e) {
                    //e.printStackTrace();
                    //如果某个客户端意外离开连接
                    try {
                        if(this.currentSocket != null&&!this.currentSocket.isClosed()){
                            this.currentSocket.close();
                            socketList.remove(this.currentSocket);
                           //获得远程客户端IP和端口Port
                            String remoteIp = currentSocket.getInetAddress().getHostAddress();
                            int remotePort = currentSocket.getPort();
                            System.out.printf("%s/%d客户端已下线 %n",remoteIp,remotePort);
                            System.out.printf("当前聊天室在线人数为：%d %n",socketList.size());


                        }
                        this.currentSocket = null;//help gc
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }

                }

            }

        }

        /**
         * 给客户端分发信息，排除自身
         * @param msg 发送消息
         */
        private void dispatch(String msg) {
            //声明打印流对象
            PrintWriter writer = null;
            for (Socket socket:socketList){
                if(Objects.equals(socket,currentSocket)){
                    continue;
                }
                //创建打印流对象
                try {
                    writer = new PrintWriter(socket.getOutputStream(),true);
                    writer.println(msg);
                    writer.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }
    }

    public static void main(String[] args) {
        startServer(SERVER_PORT);
    }
}
